/*
 * Copyright (C) 2015 yixun All Rights Reserved.
 *
 * PartnerInfoServiceImpl.java
 */
package com.yixun.qfbao.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.yixun.qfbao.model.CompanyRelation;
import com.yixun.qfbao.model.DecisionServiceEnum;
import com.yixun.qfbao.model.OverdueConfig;
import com.yixun.qfbao.model.PartnerInfo;
import com.yixun.qfbao.model.dto.*;
import com.yixun.qfbao.model.enums.*;
import com.yixun.qfbao.dao.DepartmentDao;
import com.yixun.qfbao.service.CompanyRelationService;
import com.yixun.qfbao.service.OverdueConfigService;
import com.yixun.qfbao.service.PartnerInfoService;
import com.yixun.qfbao.service.PremisesOverdueService;
import com.yixun.qfbao.xf.model.CustomerPremises;
import com.yixun.qfbao.xf.model.Premises;
import com.yixun.qfbao.xf.model.dto.CustomerPremisesDto;
import com.yixun.qfbao.xf.model.enums.CustomerStatusEnum;
import com.yixun.qfbao.xf.service.CustomerPremisesService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * 楼盘逾期校验
 *
 * @author jhsong
 * @version 1.00
 */
@Service("premisesOverdueService")
public class PremisesOverdueServiceImpl implements PremisesOverdueService {
	private Logger logger = LoggerFactory.getLogger(PartnerInfoService.class);

	@Autowired
	private OverdueConfigService overdueConfigService;
	@Autowired
	private DepartmentDao departmentDao;
	@Autowired
	private PartnerInfoService partnerInfoService;
	@Autowired
	private CustomerPremisesService customerPremisesService;
	@Autowired
	private CompanyRelationService companyRelationService;

	/**
	 * 报备保护时间逾期校验
	 * @param premises
	 * @param mobile
	 * @return
	 */
	@Override
	public ReportProtectOverDueDto checkReportProtect(Premises premises, PartnerInfo pi, String mobile) {
		ReportProtectOverDueDto overDueDto = new ReportProtectOverDueDto();
		// 判断楼盘是否有设置保护逾期
		Long reportProtectType = premises.getReportProtectType();
		Long reportProtect = premises.getReportProtect();
		// 设置楼盘本身默认报备保护规则
		overDueDto.setCanReport(Boolean.TRUE);
		overDueDto.setDecisionType(DecisionTypeEnum.TYPE1.getNo());
		overDueDto.setOverCount(0);
		overDueDto.setProtectTime(reportProtect);
		overDueDto.setTimeUnit(reportProtectType);

		// 判断是否存在保护期内的报备
		boolean checkReportProtectFalg = customerPremisesService.checkReportProtect(mobile, premises);
		if (!checkReportProtectFalg) {
			overDueDto.setCanReport(Boolean.FALSE);
			return overDueDto;
		}

		// 楼盘未设置逾期保护,直接返回
		if(Objects.isNull(reportProtectType)
			|| Long.valueOf(TimeUnitTypeEnum.NONE.getNo()).equals(reportProtectType)){
			logger.info("cpreportserviceimpl --> addReport --> checkReportProtect: 楼盘Id: {}, 手机号: {}, {}", premises.getId(), mobile, "未设置报备保护,直接返回");
			overDueDto.setProtectTime(null);
			overDueDto.setTimeUnit(null);
			return overDueDto;
		}
		getReportProtectOverDue(overDueDto, premises, pi, mobile);
		return overDueDto;
	}

	ReportProtectOverDueDto getReportProtectOverDue(ReportProtectOverDueDto overDueDto, Premises premises, PartnerInfo pi, String mobile){
		// 获取楼盘逾期配置
		OverdueConfigDto overdueConfigDto = new OverdueConfigDto();
		overdueConfigDto.setPremisesId(premises.getId());
		overdueConfigDto.setUsable(UsableEnum.USABLE.getNo());
		overdueConfigDto.setDelFlag(DelFlagEnum.UNDEL.getNo());
		overdueConfigDto.setDecisionService(DecisionServiceEnum.PEPORT_OVER_TIME.getNo());
		overdueConfigDto.setOrders("decision_sort asc");
		List<OverdueConfig> configList = overdueConfigService.selectListByDto(overdueConfigDto);
		logger.info("cpreportserviceimpl --> addReport --> getReportProtectOverDue --> 楼盘Id: {}, 手机号: {}, configList: {}", premises.getId(), mobile, configList.size());
		if(CollectionUtils.isEmpty(configList) || configList.size() == 1){
			overDueDto.setCanReport(Boolean.TRUE);
			return overDueDto;
		}

		Map<Integer,OverdueConfig> configMap = configList.stream().collect(Collectors.toMap(OverdueConfig::getDecisionSort, Function.identity(),(oldData, newData)->newData));
		logger.info("cpreportserviceimpl --> addReport --> getReportProtectOverDue --> 楼盘Id: {}, 手机号: {}, configMap: {}", premises.getId(), mobile, JSONObject.toJSONString(configMap));
		int maxSort = configList.stream().mapToInt(OverdueConfig::getDecisionSort).max().getAsInt();
		logger.info("cpreportserviceimpl --> addReport --> getReportProtectOverDue --> 楼盘Id: {}, 手机号: {}, maxSort: {}", premises.getId(), mobile, maxSort);
		OverdueConfig overdueConfig = configList.stream().filter(item -> item.getDecisionSort() == 0).findFirst().orElse(null);
		// 决策范围: 0、全部 1、机构 2、门店 3、组别 4、经纪人
		Integer decisionApply = overdueConfig.getDecisionApply();
		int deptLevel = departmentDao.getDeptLevel(pi.getDeptId());
		// 决策基本 < 用户所处部门级别: 可报备
		if(decisionApply > deptLevel){
			return overDueDto;
		}
		// 重置节点: 1、到访成功 2、认筹成功 3、认购成功 4、签约成功 5、永不重置
		Integer decisionReset = overdueConfig.getDecisionReset();
		// 获取重置节点后,处于待审且未到访的报备记录
		List<CustomerPremises> cpList = getOverReportList(premises, pi, mobile, deptLevel, decisionApply, decisionReset);
		logger.info("cpreportserviceimpl --> addReport --> getReportProtectOverDue --> 楼盘Id: {}, 手机号: {}, cpList: {}", premises.getId(), mobile, cpList.size());
		if(CollectionUtils.isEmpty(cpList)){
			return overDueDto;
		}

		// 判断报备中是否存在未过保护期的报备
		long inProtectSize = cpList.stream().filter(item -> {
			if(Objects.nonNull(item.getReportPeriodTime())){
				if(item.getReportPeriodTime().compareTo(new Date()) > 0 && !CustomerStatusEnum.no_valid.getNo().equals(item.getCustomerStatus())){
					return true;
				}
			}
			return false;
		}).count();
		logger.info("cpreportserviceimpl --> addReport --> getReportProtectOverDue --> 楼盘Id: {}, 手机号: {}, inProtectSize: {}", premises.getId(), mobile, inProtectSize);
		// 存在未过保护期的报备
		if(inProtectSize > 0){
			overDueDto.setCanReport(Boolean.FALSE);
			overDueDto.setDecisionType(DecisionTypeEnum.TYPE3.getNo());
			return overDueDto;
		}

		// 判断逾期的报备数量
		long overProtectSize = cpList.stream().filter(item -> {
			if(Objects.nonNull(item.getReportPeriodTime())){
				if(item.getReportPeriodTime().compareTo(new Date()) < 0){
					return true;
				}
			}
			return false;
		}).count();
		logger.info("cpreportserviceimpl --> addReport --> getReportProtectOverDue --> 楼盘Id: {}, 手机号: {}, overProtectSize: {}, maxSort: {}", premises.getId(), mobile, overProtectSize, maxSort);
		if(overProtectSize > maxSort){
			overProtectSize = maxSort;
		}
		overdueConfig = configMap.get(Integer.valueOf(String.valueOf(overProtectSize)));
		if(Objects.isNull(overdueConfig) || Objects.isNull(overdueConfig.getDecisionType())){
			return overDueDto;
		}
		// 根据规则类型返回相应值: 1、变更报备保护期 2、无报备保护期 3、无法报备
		// 2,3无延长时间类型,直接处理返回.
		// 1、需要根据时间单位进行换算
		if(overdueConfig.getDecisionType().equals(DecisionTypeEnum.TYPE2.getNo())){
			overDueDto.setDecisionType(DecisionTypeEnum.TYPE2.getNo());
			overDueDto.setOverCount(Integer.valueOf(String.valueOf(overProtectSize)));
			overDueDto.setProtectTime(0L);
			overDueDto.setTimeUnit(0L);
		}else if(overdueConfig.getDecisionType().equals(DecisionTypeEnum.TYPE3.getNo())){
			overDueDto.setCanReport(Boolean.FALSE);
			overDueDto.setDecisionType(DecisionTypeEnum.TYPE3.getNo());
			overDueDto.setOverCount(Integer.valueOf(String.valueOf(overProtectSize)));
			return overDueDto;
		}else if(overdueConfig.getDecisionType().equals(DecisionTypeEnum.TYPE1.getNo())){
			int timeType = overdueConfig.getDecisionTimeType().intValue();
			overDueDto.setOverCount(Integer.valueOf(String.valueOf(overProtectSize)));
			// 判断时间规则,转换成分钟
			overDueDto.setProtectTime(getTimebyType(overdueConfig.getDecisionTime(), timeType));
			overDueDto.setTimeUnit(overdueConfig.getDecisionTimeType().longValue());
		}
		return overDueDto;
	}

	@Override
	public ReportValidOverDueDto checkReportValid(Premises premises, PartnerInfo pi, String mobile) {
		ReportValidOverDueDto overDueDto = new ReportValidOverDueDto();
		Long reportValidType = premises.getReportValidType();
		Long reportValid = premises.getReportValid();
		// 设置楼盘本身默认报备有效规则
		overDueDto.setCanReport(Boolean.TRUE);
		overDueDto.setDecisionType(DecisionTypeEnum.TYPE1.getNo());
		overDueDto.setOverCount(0);
		overDueDto.setValidTime(reportValid);
		overDueDto.setTimeUnit(reportValidType);
		// 楼盘未设置逾期保护,直接返回
		if(Objects.isNull(reportValidType)
				|| Long.valueOf(TimeUnitTypeEnum.EXTEND_05.getNo()).equals(reportValidType)){
			logger.info("cpreportserviceimpl --> addReport --> 楼盘Id: {}, 手机号: {}, checkReportProtect: {}", premises.getId(), mobile, "楼盘未设置逾期保护,直接返回");
			overDueDto.setValidTime(null);
			overDueDto.setTimeUnit(null);
			return overDueDto;
		}
		getReportValidOverDue(overDueDto, premises, pi, mobile);
		return overDueDto;
	}

	ReportValidOverDueDto getReportValidOverDue(ReportValidOverDueDto overDueDto, Premises premises, PartnerInfo pi, String mobile){
		// 获取楼盘逾期配置
		OverdueConfigDto overdueConfigDto = new OverdueConfigDto();
		overdueConfigDto.setPremisesId(premises.getId());
		overdueConfigDto.setUsable(UsableEnum.USABLE.getNo());
		overdueConfigDto.setDelFlag(DelFlagEnum.UNDEL.getNo());
		overdueConfigDto.setDecisionService(DecisionServiceEnum.PEPORT_VALID_TIME.getNo());
		overdueConfigDto.setOrders("decision_sort asc");
		List<OverdueConfig> configList = overdueConfigService.selectListByDto(overdueConfigDto);
		logger.info("cpreportserviceimpl --> addReport --> getReportValidOverDue -->楼盘Id: {}, 手机号: {}, configList: {}", premises.getId(), mobile, configList.size());
		if(CollectionUtils.isEmpty(configList) || configList.size() == 1){
			return overDueDto;
		}

		Map<Integer,OverdueConfig> configMap = configList.stream().collect(Collectors.toMap(OverdueConfig::getDecisionSort, Function.identity(),(oldData, newData)->newData));
		logger.info("cpreportserviceimpl --> addReport --> getReportValidOverDue --> 楼盘Id: {}, 手机号: {}, configMap: {}", premises.getId(), mobile, JSONObject.toJSONString(configMap));
		int maxSort = configList.stream().mapToInt(OverdueConfig::getDecisionSort).max().getAsInt();
		logger.info("cpreportserviceimpl --> addReport --> getReportValidOverDue --> 楼盘Id: {}, 手机号: {}, maxSort: {}", premises.getId(), mobile, maxSort);
		OverdueConfig overdueConfig = configList.stream().filter(item -> item.getDecisionSort() == 0).findFirst().orElse(null);
		// 决策范围: 0、全部 1、机构 2、门店 3、组别 4、经纪人
		Integer decisionApply = overdueConfig.getDecisionApply();
		int deptLevel = departmentDao.getDeptLevel(pi.getDeptId());
		// 当前用户部门层级 > 决策级别~可报备
		if(deptLevel < decisionApply){
			return overDueDto;
		}
		// 重置节点: 1、到访成功 2、认筹成功 3、认购成功 4、签约成功 5、永不重置
		Integer decisionReset = overdueConfig.getDecisionReset();
		// 获取重置节点后
		List<CustomerPremises> cpList = getOverReportList(premises, pi, mobile, deptLevel, decisionApply, decisionReset);
		logger.info("cpreportserviceimpl --> addReport --> getReportValidOverDue -->楼盘Id: {},手机号: {}, cpList: {}", premises.getId(), mobile, cpList.size());
		if(CollectionUtils.isEmpty(cpList)){
			return overDueDto;
		}

		// 判断逾期的报备数量
		long invalidSize = cpList.stream().filter(item -> {
			if(Objects.nonNull(item.getReportValidTime())){
				if(item.getReportValidTime().compareTo(new Date()) < 0){
					return true;
				}
			}
			return false;
		}).count();
		logger.info("cpreportserviceimpl --> addReport --> getReportValidOverDue --> 楼盘Id: {}, 手机号: {}, overProtectSize: {}, maxSort: {}", premises.getId(), mobile, invalidSize, maxSort);
		if(invalidSize > maxSort){
			invalidSize = maxSort;
		}
		overdueConfig = configMap.get(Integer.valueOf(String.valueOf(invalidSize)));
		if(Objects.isNull(overdueConfig) || Objects.isNull(overdueConfig.getDecisionType())){
			return overDueDto;
		}
		// 根据规则类型返回相应值: 1、变更报备保护期 2、无报备保护期 3、无法报备
		// 2,3无延长时间类型,直接处理返回.
		// 1、需要根据时间单位进行换算 -- 有效期只有 TYPE1 的设置
		if(overdueConfig.getDecisionType().equals(DecisionTypeEnum.TYPE2.getNo())){
			overDueDto.setDecisionType(DecisionTypeEnum.TYPE2.getNo());
			overDueDto.setOverCount(Integer.valueOf(String.valueOf(invalidSize)));
			overDueDto.setValidTime(0L);
			overDueDto.setTimeUnit(0L);
		}else if(overdueConfig.getDecisionType().equals(DecisionTypeEnum.TYPE3.getNo())){
			overDueDto.setCanReport(Boolean.FALSE);
			overDueDto.setDecisionType(DecisionTypeEnum.TYPE3.getNo());
			overDueDto.setOverCount(Integer.valueOf(String.valueOf(invalidSize)));
		}else if(overdueConfig.getDecisionType().equals(DecisionTypeEnum.TYPE1.getNo())){
			int timeType = overdueConfig.getDecisionTimeType().intValue();
			overDueDto.setOverCount(Integer.valueOf(String.valueOf(invalidSize)));
			if(TimeUnitTypeEnum.NONE.getNo().equals(String.valueOf(timeType))){
				overDueDto.setValidTime(0L);
				overDueDto.setTimeUnit(0L);
                return overDueDto;
            }
			if(TimeUnitTypeEnum.EXTEND_05.getNo().equals(String.valueOf(timeType))){
				overDueDto.setValidTime(null);
				overDueDto.setTimeUnit(null);
				return overDueDto;
			}
			// 判断时间规则,转换成分钟
			overDueDto.setValidTime(getTimebyType(overdueConfig.getDecisionTime(), timeType));
			overDueDto.setTimeUnit(overdueConfig.getDecisionTimeType().longValue());
		}
		return overDueDto;
	}

	/**
	 * 获取报备逾期重置后记录
	 * @param premises
	 * @param pi
	 * @param mobile
	 * @param deptLevel
	 * @param decisionApply
	 * @param decisionReset
	 * @return
	 */
	public List<CustomerPremises> getOverReportList(Premises premises, PartnerInfo pi, String mobile, int deptLevel, Integer decisionApply, Integer decisionReset){
		CustomerPremisesDto customerPremisesDto = new CustomerPremisesDto();
		customerPremisesDto.setPremisesId(premises.getId());
		customerPremisesDto.setMobile(mobile);
		if(DecisionApplyEnum.ALL.getNo().equals(decisionApply)){
			CompanyRelationDto companyRelationDto = new CompanyRelationDto();
			companyRelationDto.setDevCompany(premises.getCompanyId());
			List<CompanyRelation> companyRelationList = companyRelationService.selectListByDto(companyRelationDto);
			List<Long> staffCompanyIds = companyRelationList.stream()
					.map(CompanyRelation::getAgentCompany)
					.collect(Collectors.toList());
			if(CollectionUtils.isEmpty(staffCompanyIds)){
				return null;
			}
			customerPremisesDto.setStaffCompanyIds(staffCompanyIds);
		} else if(DecisionApplyEnum.ORGANIZATION.getNo().equals(decisionApply)) {
			customerPremisesDto.setStaffCompanyId(pi.getHigherid());
		} else if (DecisionApplyEnum.STORE.getNo().equals(decisionApply)
				|| DecisionApplyEnum.GROUP.getNo().equals(decisionApply)){
			// 门店、组别下所有的经纪人
			List<Long> deptList = departmentDao.getDeptIdsWithLevel(deptLevel,pi.getDeptId(),decisionApply);
			if(!CollectionUtils.isEmpty(deptList)) {
				PartnerInfoDto partnerInfoDto = new PartnerInfoDto();
				partnerInfoDto.setDeptIds(deptList);
				List<PartnerInfo> piList = partnerInfoService.selectListByDto(partnerInfoDto);
				List<Long> piIdList = piList.stream().map(PartnerInfo::getId).collect(Collectors.toList());
				customerPremisesDto.setStaffIds(piIdList);
			}
		} else if (DecisionApplyEnum.AGENT.getNo().equals(decisionApply)) {
			customerPremisesDto.setStaffId(pi.getId());
		}
		// 默认需要重置: 倒序获取最新的报备
		customerPremisesDto.setOrders(" ful.id desc");
		if(decisionReset != 0 ){
			switch (decisionReset){
				case 1:
					// 签约、认购、认筹、到访时间,只要有一个不为空,即标识到访过
					customerPremisesDto.setSql("(cp.sign_time is not null or cp.subscribe_time is not null or cp.pay_deposit_time is not null or cp.visit_time is not null)");
					break;
				case 2:
					// 认筹或者曾经认筹,当前失效状态.重置节点
					customerPremisesDto.setSql("(cp.sign_time is not null or cp.subscribe_time is not null or cp.pay_deposit_time is not null)");
					break;
				case 3:
					// 认购或者曾经认购,当前失效状态.重置节点
					customerPremisesDto.setSql("(cp.sign_time is not null or cp.subscribe_time is not null)");
					break;
				case 4:
					// 签约或者曾经签约,当前失效状态.重置节点
					customerPremisesDto.setSql("(cp.sign_time is not null)");
					break;
				case 5:
					// 不用重置,按顺序获取数据即可
					customerPremisesDto.setOrders(" ful.id ");
			}
		}
		// 重置节点的报备记录,若无重置节点,获取的报备记录就是最早进行报备的数据
        logger.info("cpreportserviceimpl --> addReport --> getReportProtectOverDue --> 楼盘Id: {}, 手机号: {}, selectResetNode: {}", premises.getId(), mobile, JSONObject.toJSONString(customerPremisesDto));
        CustomerPremises cp = customerPremisesService.selectResetNode(customerPremisesDto);
		if(Objects.nonNull(cp)){
			Date resetDate = cp.getCreateTime();
			if(DecisionResetEnum.VISIT_SUCCESS.getNo() == decisionReset.intValue() && Objects.nonNull(cp.getVisitTime())){
				resetDate = cp.getVisitTime();
			}else if(DecisionResetEnum.PAY_DEPOSTI_SUCCESS.getNo() == decisionReset.intValue() && Objects.nonNull(cp.getPayDepositTime())){
				resetDate = cp.getPayDepositTime();
			}else if(DecisionResetEnum.SUBSCRIBE_SUCCESS.getNo() == decisionReset.intValue() && Objects.nonNull(cp.getSubscribeTime())){
				resetDate = cp.getSubscribeTime();
			}else if(DecisionResetEnum.SIGN_SUCCESS.getNo() == decisionReset.intValue() && Objects.nonNull(cp.getSignTime())){
				resetDate = cp.getSignTime();
			} else if(DecisionResetEnum.NONE.getNo() == decisionReset.intValue()){
				// 无重置节点时,记录需要包含自身.因此需要减1秒
				Calendar calendar = Calendar.getInstance();
				calendar.setTime(resetDate);
				calendar.add(Calendar.SECOND, -1);
				resetDate = calendar.getTime();
			}
			customerPremisesDto.setCreateTimeBefore(resetDate);
		}
		customerPremisesDto.setDelFlag(null);
		customerPremisesDto.setConfirmStatus(null);
		customerPremisesDto.setSql(null);
		customerPremisesDto.setOrders("id");
        logger.info("cpreportserviceimpl --> addReport --> getReportProtectOverDue --> 楼盘Id: {}, 手机号: {}, selectListByDto: {}", premises.getId(), mobile, JSONObject.toJSONString(customerPremisesDto));
		return customerPremisesService.selectListByDto(customerPremisesDto);
	}

	private static Long getTimebyType(Long decisionTime, Integer decisionTimeType){
		Long protectTime = null;
		if(Objects.isNull(decisionTime)){
			return protectTime;
		}
		if(TimeUnitTypeEnum.DAY.getNo().equals(decisionTimeType.toString())){
			protectTime = decisionTime * 24 * 60;
		}else if(TimeUnitTypeEnum.HOUR.getNo().equals(decisionTimeType.toString())){
			protectTime = decisionTime * 60;
		}else if(TimeUnitTypeEnum.MINUT.getNo().equals(decisionTimeType.toString())){
			protectTime = decisionTime;
		}
		return protectTime;
	}
}
